package com.kexio.gen.api;

import java.io.File;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kexio.auth.annotation.RequiresPermission;
import com.kexio.common.dto.PageResponse;
import com.kexio.common.dto.Result;
import com.kexio.common.page.PageConverters;
import com.kexio.common.page.PageQuery;
import com.kexio.common.page.PageUtils;
import com.kexio.gen.domain.entity.GenTable;
import com.kexio.gen.domain.entity.GenTableColumn;
import com.kexio.gen.service.CodeGenService;
import com.kexio.gen.service.ConfigValidationService;
import com.kexio.gen.service.GenTableColumnService;
import com.kexio.gen.service.GenTableService;

@RestController
@RequestMapping("/gen")
public class GeneratorController {

    private final GenTableService genTableService;
    private final GenTableColumnService genTableColumnService;
    private final DataSource dataSource;
    private final CodeGenService codeGenService;
    private final ConfigValidationService configValidationService;

    public GeneratorController(GenTableService genTableService,
                               GenTableColumnService genTableColumnService,
                               DataSource dataSource,
                               CodeGenService codeGenService,
                               ConfigValidationService configValidationService) {
        this.genTableService = genTableService;
        this.genTableColumnService = genTableColumnService;
        this.dataSource = dataSource;
        this.codeGenService = codeGenService;
        this.configValidationService = configValidationService;
    }

   @RequiresPermission("dev:gen:manage")
    @GetMapping("/tables")
    public Result<PageResponse<GenTable>> listTables(@RequestParam(required = false) String module,
                                                     PageQuery pageQuery) {
        long current = PageUtils.normalizePageNo(pageQuery.getCurrent());
        long size = PageUtils.normalizePageSize(pageQuery.getSize());
        QueryWrapper<GenTable> qw = new QueryWrapper<>();
        if (module != null && !module.isEmpty()) {
            qw.eq("module_name", module);
        }
        Page<GenTable> page = genTableService.page(new Page<>(current, size), qw);
        PageResponse<GenTable> resp = PageConverters.map(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize(), g -> g);
        return Result.success(resp);
    }

    @com.kexio.auth.annotation.RequiresPermission("dev:gen:manage")
    @GetMapping("/db/tables")
    public Result<List<String>> listDatabaseTables(@RequestParam(required = false) String schema,
                                                   @RequestParam(required = false, name = "nameLike") String nameLike) {
        List<String> names = new ArrayList<>();
        String pattern = (nameLike == null || nameLike.isBlank()) ? "%" : (nameLike.replace('%', ' ').trim() + "%");
        try (Connection conn = dataSource.getConnection()) {
            String useSchema = (schema != null && !schema.isBlank()) ? schema : conn.getSchema();
            DatabaseMetaData meta = conn.getMetaData();
            try (ResultSet rs = meta.getTables(conn.getCatalog(), useSchema, pattern, new String[] {"TABLE"})) {
                while (rs.next()) {
                    String tbl = rs.getString("TABLE_NAME");
                    if (tbl != null && !tbl.isBlank()) {
                        names.add(tbl);
                    }
                }
            }
        } catch (Exception e) {
            return Result.error("introspect tables failed: " + e.getMessage());
        }
        return Result.success(names);
    }

   @RequiresPermission("dev:gen:manage")
    @GetMapping("/tables/{id}")
    public Result<GenTable> getTable(@PathVariable Long id) {
        GenTable table = genTableService.getById(id);
        return table == null ? Result.notFound("Not found") : Result.success(table);
    }

   @RequiresPermission("dev:gen:manage")
    @PostMapping("/tables")
    public Result<Boolean> saveTable(@RequestBody GenTable table) {
        // 数据验证
        if (table.getTableName() == null || table.getTableName().trim().isEmpty()) {
            return Result.error("表名不能为空");
        }
        
        if (table.getModuleName() == null || table.getModuleName().trim().isEmpty()) {
            return Result.error("模块名不能为空");
        }
        
        if (table.getEntityName() == null || table.getEntityName().trim().isEmpty()) {
            return Result.error("实体名不能为空");
        }
        
        if (table.getBasePackage() == null || table.getBasePackage().trim().isEmpty()) {
            return Result.error("基础包名不能为空");
        }
        
        // 规范化数据
        table.setTableName(table.getTableName().trim());
        table.setModuleName(table.getModuleName().trim());
        table.setEntityName(table.getEntityName().trim());
        table.setBasePackage(table.getBasePackage().trim());
        
        try {
            boolean ok = genTableService.saveOrUpdate(table);
            return ok ? Result.success(true) : Result.error("保存失败");
        } catch (Exception e) {
            // 处理唯一约束冲突
            if (e.getMessage() != null && e.getMessage().contains("uk_gen_table")) {
                return Result.error("表名和模块名的组合已存在，请检查是否重复配置");
            }
            return Result.error("保存失败: " + e.getMessage());
        }
    }

   @RequiresPermission("dev:gen:manage")
    @GetMapping("/tables/{id}/columns")
    public Result<List<GenTableColumn>> listColumns(@PathVariable Long id) {
        List<GenTableColumn> list = genTableColumnService.list(new QueryWrapper<GenTableColumn>().eq("table_id", id));
        return Result.success(list);
    }

   @RequiresPermission("dev:gen:manage")
    @PostMapping("/tables/{id}/columns/batch")
    public Result<Boolean> saveColumns(@PathVariable Long id, @RequestBody List<GenTableColumn> columns) {
        for (GenTableColumn c : columns) {
            c.setTableId(id);
        }
        boolean ok = genTableColumnService.saveOrUpdateBatch(columns);
        return ok ? Result.success(true) : Result.error("Save failed");
    }

    @com.kexio.auth.annotation.RequiresPermission("dev:gen:manage")
    @DeleteMapping("/tables/{id}")
    public Result<Boolean> deleteTable(@PathVariable Long id) {
        // 先删除关联的字段配置
        genTableColumnService.remove(new QueryWrapper<GenTableColumn>().eq("table_id", id));
        // 再删除表配置
        boolean ok = genTableService.removeById(id);
        return ok ? Result.success(true) : Result.error("Delete failed");
    }

    @com.kexio.auth.annotation.RequiresPermission("dev:gen:manage")
    @PostMapping("/tables/batch-delete")
    public Result<Boolean> deleteTables(@RequestBody List<Long> ids) {
        if (ids == null || ids.isEmpty()) {
            return Result.badRequest("ids is empty");
        }
        // 先批量删除关联的字段配置
        for (Long id : ids) {
            genTableColumnService.remove(new QueryWrapper<GenTableColumn>().eq("table_id", id));
        }
        // 再批量删除表配置
        boolean ok = genTableService.removeBatchByIds(ids);
        return ok ? Result.success(true) : Result.error("Batch delete failed");
    }

    @com.kexio.auth.annotation.RequiresPermission("dev:gen:manage")
    @DeleteMapping("/tables/{id}/columns/{colId}")
    public Result<Boolean> deleteColumn(@PathVariable Long id, @PathVariable Long colId) {
        boolean ok = genTableColumnService.removeById(colId);
        return ok ? Result.success(true) : Result.error("Delete failed");
    }

    // Placeholders for next steps
   @RequiresPermission("dev:gen:manage")
    @PostMapping("/tables/{id}/introspect-columns")
    public Result<String> introspect(@PathVariable Long id) {
        GenTable table = genTableService.getById(id);
        if (table == null) {
            return Result.notFound("Table not found");
        }
        int added = genTableColumnService.introspectAndMerge(id, table.getTableName());
        return Result.success("introspected:" + added);
    }

   @RequiresPermission("dev:gen:manage")
    @PostMapping("/tables/{id}/validate-query-defs")
    public Result<String> validateQueryDefs(@PathVariable Long id) {
        GenTable table = genTableService.getById(id);
        if (table == null) {
            return Result.notFound("Table not found");
        }
        Object defs = table.getQueryDefs();
        if (defs == null) {
            return Result.badRequest("query_defs is null");
        }
        // 最小校验：只是检查包含字符串 "type" 和 "queries"
        String s = String.valueOf(defs);
        if (!s.contains("queries")) {
            return Result.badRequest("query_defs missing 'queries'");
        }
        return Result.success("ok");
    }

   @RequiresPermission("dev:gen:preview")
    @PostMapping("/tables/{id}/preview")
    public Result<Object> preview(@PathVariable Long id) {
        GenTable table = genTableService.getById(id);
        if (table == null) {
            return Result.notFound("Table not found");
        }
        List<GenTableColumn> columns = genTableColumnService.list(new QueryWrapper<GenTableColumn>().eq("table_id", id));
        List<Map<String, String>> files = codeGenService.previewFiles(table, columns);

        Map<String, Object> payload = new HashMap<>();
        payload.put("tableId", id);
        payload.put("files", files);
        return Result.success(payload);
    }

    // removed local addPreview; now using CodeGenService

   @RequiresPermission("dev:gen:execute")
    @PostMapping("/tables/{id}/generate")
    public Result<String> generate(@PathVariable Long id) {
        GenTable table = genTableService.getById(id);
        if (table == null) {
            return Result.notFound("Table not found");
        }
        List<GenTableColumn> columns = genTableColumnService.list(new QueryWrapper<GenTableColumn>().eq("table_id", id));
        String result = codeGenService.generate(table, columns);
        return Result.success(result);
    }
    
    @RequiresPermission("dev:gen:execute")
    @PostMapping("/tables/{id}/download-zip")
    public ResponseEntity<byte[]> downloadZip(@PathVariable Long id) {
        GenTable table = genTableService.getById(id);
        if (table == null) {
            return ResponseEntity.notFound().build();
        }
        
        List<GenTableColumn> columns = genTableColumnService.list(new QueryWrapper<GenTableColumn>().eq("table_id", id));
        List<Map<String, String>> files = codeGenService.previewFiles(table, columns);
        
        try {
            String fileName = table.getModuleName() + "-" + table.getEntityName() + "-code.zip";
            
            try (java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
                 java.util.zip.ZipOutputStream zipOut = new java.util.zip.ZipOutputStream(baos)) {
                
                for (Map<String, String> file : files) {
                    String path = file.get("path");
                    String content = file.get("content");
                    
                    if (path != null && content != null) {
                        zipOut.putNextEntry(new java.util.zip.ZipEntry(path));
                        zipOut.write(content.getBytes("UTF-8"));
                        zipOut.closeEntry();
                    }
                }
                zipOut.flush();
                
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
                headers.setContentDispositionFormData("attachment", fileName);
                
                return ResponseEntity.ok()
                    .headers(headers)
                    .body(baos.toByteArray());
            }
        } catch (Exception e) {
            return ResponseEntity.status(500).body(("生成压缩包失败: " + e.getMessage()).getBytes());
        }
    }
    
    @RequiresPermission("dev:gen:execute")
    @PostMapping("/tables/{id}/generate-to-project")
    public Result<String> generateToProject(@PathVariable Long id, @RequestBody Map<String, Object> request) {
        GenTable table = genTableService.getById(id);
        if (table == null) {
            return Result.notFound("Table not found");
        }
        
        String targetModule = (String) request.get("targetModule");
        Boolean overwrite = (Boolean) request.getOrDefault("overwrite", false);
        
        if (targetModule == null || targetModule.trim().isEmpty()) {
            return Result.badRequest("目标模块名不能为空");
        }
        
        try {
            List<GenTableColumn> columns = genTableColumnService.list(new QueryWrapper<GenTableColumn>().eq("table_id", id));
            List<Map<String, String>> files = codeGenService.previewFiles(table, columns);
            
            String projectRoot = System.getProperty("user.dir");
            String targetPath = projectRoot + File.separator + targetModule;
            
            // 检查目标模块目录是否存在
            File targetDir = new File(targetPath);
            if (!targetDir.exists()) {
                return Result.error("目标模块目录不存在: " + targetPath);
            }
            
            int generatedCount = 0;
            int skippedCount = 0;
            
            for (Map<String, String> file : files) {
                String relativePath = file.get("path");
                String content = file.get("content");
                
                if (relativePath == null || content == null) continue;
                
                // 构建完整路径
                String fullPath;
                if (relativePath.startsWith("src/")) {
                    fullPath = targetPath + File.separator + relativePath;
                } else if (relativePath.startsWith("resources/")) {
                    fullPath = targetPath + File.separator + "src" + File.separator + "main" + File.separator + relativePath;
                } else {
                    fullPath = targetPath + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator + relativePath;
                }
                
                File targetFile = new File(fullPath);
                
                // 检查文件是否存在
                if (targetFile.exists() && !overwrite) {
                    skippedCount++;
                    continue;
                }
                
                // 创建父目录
                targetFile.getParentFile().mkdirs();
                
                // 写入文件
                try (java.io.FileWriter writer = new java.io.FileWriter(targetFile, false)) {
                    writer.write(content);
                    generatedCount++;
                }
            }
            
            String result = String.format("生成完成！目标模块: %s, 生成文件: %d个, 跳过文件: %d个", 
                targetModule, generatedCount, skippedCount);
            return Result.success(result);
            
        } catch (Exception e) {
            return Result.error("生成到项目模块失败: " + e.getMessage());
        }
    }
    
    @RequiresPermission("dev:gen:preview")
    @PostMapping("/tables/batch-preview")
    public Result<Map<String, Object>> batchPreview(@RequestBody Map<String, Object> request) {
        @SuppressWarnings("unchecked")
        List<Long> tableIds = (List<Long>) request.get("tableIds");
        
        if (tableIds == null || tableIds.isEmpty()) {
            return Result.badRequest("表ID列表不能为空");
        }
        
        Map<String, List<Map<String, String>>> results = new HashMap<>();
        List<String> errors = new ArrayList<>();
        
        for (Long tableId : tableIds) {
            try {
                GenTable table = genTableService.getById(tableId);
                if (table != null) {
                    List<GenTableColumn> columns = genTableColumnService.list(
                        new QueryWrapper<GenTableColumn>().eq("table_id", tableId));
                    List<Map<String, String>> files = codeGenService.previewFiles(table, columns);
                    results.put(table.getTableName(), files);
                }
            } catch (Exception e) {
                errors.add("表 " + tableId + ": " + e.getMessage());
            }
        }
        
        Map<String, Object> response = new HashMap<>();
        response.put("results", results);
        response.put("errors", errors);
        return Result.success(response);
    }
    
    @RequiresPermission("dev:gen:execute")
    @PostMapping("/tables/batch-generate")
    public Result<Map<String, Object>> batchGenerate(@RequestBody Map<String, Object> request) {
        @SuppressWarnings("unchecked")
        List<Long> tableIds = (List<Long>) request.get("tableIds");
        
        if (tableIds == null || tableIds.isEmpty()) {
            return Result.badRequest("表ID列表不能为空");
        }
        
        Map<String, String> results = new HashMap<>();
        List<String> errors = new ArrayList<>();
        int successCount = 0;
        
        for (Long tableId : tableIds) {
            try {
                GenTable table = genTableService.getById(tableId);
                if (table != null) {
                    List<GenTableColumn> columns = genTableColumnService.list(
                        new QueryWrapper<GenTableColumn>().eq("table_id", tableId));
                    String result = codeGenService.generate(table, columns);
                    results.put(table.getTableName(), result);
                    successCount++;
                }
            } catch (Exception e) {
                errors.add("表 " + tableId + ": " + e.getMessage());
            }
        }
        
        Map<String, Object> response = new HashMap<>();
        response.put("results", results);
        response.put("errors", errors);
        response.put("successCount", successCount);
        response.put("totalCount", tableIds.size());
        return Result.success(response);
    }
    
    @RequiresPermission("dev:gen:manage")
    @PostMapping("/tables/{id}/validate-config")
    public Result<Object> validateConfig(@PathVariable Long id) {
        GenTable table = genTableService.getById(id);
        if (table == null) {
            return Result.notFound("Table not found");
        }
        
        List<GenTableColumn> columns = genTableColumnService.list(
            new QueryWrapper<GenTableColumn>().eq("table_id", id));
        
        ConfigValidationService.ValidationResult result = 
            configValidationService.validateConfig(table, columns);
            
        Map<String, Object> response = new HashMap<>();
        response.put("hasErrors", result.hasErrors());
        response.put("errors", result.getErrors());
        response.put("warnings", result.getWarnings());
        
        return Result.success(response);
    }
    
    @RequiresPermission("dev:gen:manage")
    @PostMapping("/tables/recommend-config")
    public Result<Object> recommendConfig(@RequestBody Map<String, Object> request) {
        String tableName = (String) request.get("tableName");
        @SuppressWarnings("unchecked")
        List<Map<String, Object>> columnData = (List<Map<String, Object>>) request.get("columns");
        
        if (tableName == null || tableName.trim().isEmpty()) {
            return Result.badRequest("表名不能为空");
        }
        
        // 转换为GenTableColumn对象
        List<GenTableColumn> columns = new ArrayList<>();
        if (columnData != null) {
            for (Map<String, Object> col : columnData) {
                GenTableColumn column = new GenTableColumn();
                column.setColumnName((String) col.get("columnName"));
                column.setPropertyName((String) col.get("propertyName"));
                column.setJavaType((String) col.get("javaType"));
                column.setComment((String) col.get("comment"));
                columns.add(column);
            }
        }
        
        ConfigValidationService.ConfigRecommendation recommendation = 
            configValidationService.recommendConfig(tableName, columns);
            
        return Result.success(recommendation);
    }
}


